Multi-category dot-density maps often work well when the categories cluster geographically. Recent immigrants by country of origin work well for this. ## Data First we grab the immigrant data via cancensus, making use of the CensusMapper API tool to select the regions and variables we need.
#devtools::install_github("mountainmath/cancensus")
library(cancensus)
library(dotdensity)
# options(cancensus.api_key='your_api_key')
regions=list(CMA="59933")
vectors=c("v_CA11N_265","v_CA11N_268","v_CA11N_304","v_CA11N_334","v_CA11N_373","v_CA11N_376","v_CA11N_379","v_CA11N_382")
We choose the categories and colours we want to map and define a convenience function to rename the variables and compute the qantities for the other asian countries that we don’t break out.
categories=c("Americas","Europe","Africa","Philippines","China","India","Other Asian Countries")
colors=c("#7a0177", "#3333cc", "#ff00ff", "#00ffff", "#ff1a1c", "#4dff4a", "#ffff33")
prep_data <- function(geo){
data <- geo@data %>% replace(is.na(.), 0)
data <- rename(data,
total=v_CA11N_265,
Americas=v_CA11N_268,
Europe=v_CA11N_304,
Africa=v_CA11N_334,
Philippines=v_CA11N_376,
China=v_CA11N_379,
India=v_CA11N_382)
data <- mutate(data,`Other Asian Countries` = v_CA11N_373-Philippines-China-India)
geo@data <- data
return(geo)
}
Next we grab the data via cancensus,
data_csd=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CSD') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_5944ecb0017405460024293a4e1245b4.geojson", layer: "OGRGeoJSON"
with 39 features
It has 12 fields
data_ct=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='CT') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_2c2ba582c4fbc552cd7375e1cdb94585.geojson", layer: "OGRGeoJSON"
with 457 features
It has 12 fields
data_da=get_census(dataset = 'CA11', regions=regions,vectors=vectors,geo_format='sp',labels='short',level='DA') %>% prep_data
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_6b5f32d4a2ad20b88dbcddc4dc419562.geojson", layer: "OGRGeoJSON"
with 3438 features
It has 12 fields
data_db=get_census(dataset = 'CA11', regions=regions,geo_format='sp',labels='short',level='DB')
OGR data source with driver: GeoJSON
Source: "data_cache/CM_geo_1cc7cbd8f3815230304992d45a56ef3b.geojson", layer: "OGRGeoJSON"
with 15479 features
It has 10 fields
which we then re-aggregate to make sure we don’t miss overall counts due to privacy cutoffs distribute them proportionally among the population.
data_ct@data <- dot_density.proportional_re_aggregate(data=data_ct@data,parent_data=data_csd@data,geo_match=setNames("GeoUID","CSD_UID"),categories=categories,base="Population")
data_da@data <- dot_density.proportional_re_aggregate(data=data_da@data,parent_data=data_ct@data,geo_match=setNames("GeoUID","CT_UID"),categories=categories,base="Population")
data_db@data <- dot_density.proportional_re_aggregate(data=data_db@data,parent_data=data_da@data,geo_match=setNames("GeoUID","DA_UID"),categories=categories,base="Population")
Map
All that’s left to do is to covert our re-aggregated block-level data to dots, using the dot_density.compute_dots function from the dotdensity package and feed it into the dot_density.dots_map function to add them to our basemap.
# 1 dot = 5 immigrants
scale=5
#xlim = c(-123.34, -122.51), ylim = c(49, 49.4)
dots.db <- dot_density.compute_dots(geo_data = data_db, categories = categories, scale=scale)
basemap +
coord_fixed(xlim=c(-123.29,-122.6), ylim=c(49.02,49.35), ratio = 1/cos(49.2/180*pi)) +
# shade unpopulated blocks
# geom_polygon(data=data_db[data_db$Population<=5,],
# aes(long, lat, group = group),
# fill = "#222222", size=0.1,
# color = "#222222") +
scale_colour_manual(values = colors) +
labs(color = "",
title="Immigrants 2006 - 2011",
caption="Source: StatCan Census 2016 via cancensus & CensusMapper.ca",
subtitle = paste0("1 dot = ",scale," people")) +
dot_density.dots_map(dots=dots.db,alpha=0.75,size=0.25)

# save image for later
ggsave('../images/recent_immigrants.png',width=26,height=26)
LS0tCnRpdGxlOiAiUmVjZW50IEltbWlncmFudHMiCmF1dGhvcjogIkplbnMgdm9uIEJlcmdtYW5uIgpkYXRlOiAiMjAxNy0wOC0yNiIKb3V0cHV0OiBodG1sX25vdGVib29rCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7UmVjZW50IEltbWlncmFudHN9CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCgpNdWx0aS1jYXRlZ29yeSBkb3QtZGVuc2l0eSBtYXBzIG9mdGVuIHdvcmsgd2VsbCB3aGVuIHRoZSBjYXRlZ29yaWVzIGNsdXN0ZXIgZ2VvZ3JhcGhpY2FsbHkuIFJlY2VudCBpbW1pZ3JhbnRzIGJ5CmNvdW50cnkgb2Ygb3JpZ2luIHdvcmsgd2VsbCBmb3IgdGhpcy4KIyMgRGF0YQpGaXJzdCB3ZSBncmFiIHRoZSBpbW1pZ3JhbnQgZGF0YSB2aWEgW2NhbmNlbnN1c10oaHR0cHM6Ly9naXRodWIuY29tL21vdW50YWluTWF0aC9jYW5jZW5zdXMpLCBtYWtpbmcgdXNlIG9mIHRoZSBbQ2Vuc3VzTWFwcGVyIEFQSSB0b29sXShodHRwczovL2NlbnN1c21hcHBlci5jYS9hcGkvQ0ExMSkgdG8gc2VsZWN0IHRoZSByZWdpb25zIGFuZCB2YXJpYWJsZXMgd2UgbmVlZC4KYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIm1vdW50YWlubWF0aC9jYW5jZW5zdXMiKQpsaWJyYXJ5KGNhbmNlbnN1cykKbGlicmFyeShkb3RkZW5zaXR5KQojIG9wdGlvbnMoY2FuY2Vuc3VzLmFwaV9rZXk9J3lvdXJfYXBpX2tleScpCnJlZ2lvbnM9bGlzdChDTUE9IjU5OTMzIikKdmVjdG9ycz1jKCJ2X0NBMTFOXzI2NSIsInZfQ0ExMU5fMjY4Iiwidl9DQTExTl8zMDQiLCJ2X0NBMTFOXzMzNCIsInZfQ0ExMU5fMzczIiwidl9DQTExTl8zNzYiLCJ2X0NBMTFOXzM3OSIsInZfQ0ExMU5fMzgyIikKYGBgCgpXZSBjaG9vc2UgdGhlIGNhdGVnb3JpZXMgYW5kIGNvbG91cnMgd2Ugd2FudCB0byBtYXAgYW5kIGRlZmluZSBhIGNvbnZlbmllbmNlIGZ1bmN0aW9uIHRvIHJlbmFtZSB0aGUgdmFyaWFibGVzIGFuZCBjb21wdXRlIHRoZSBxYW50aXRpZXMgZm9yIHRoZSBvdGhlciBhc2lhbiBjb3VudHJpZXMgdGhhdCB3ZSBkb24ndCBicmVhayBvdXQuCmBgYHtyfQpjYXRlZ29yaWVzPWMoIkFtZXJpY2FzIiwiRXVyb3BlIiwiQWZyaWNhIiwiUGhpbGlwcGluZXMiLCJDaGluYSIsIkluZGlhIiwiT3RoZXIgQXNpYW4gQ291bnRyaWVzIikKY29sb3JzPWMoIiM3YTAxNzciLCAiIzMzMzNjYyIsICIjZmYwMGZmIiwgIiMwMGZmZmYiLCAiI2ZmMWExYyIsICIjNGRmZjRhIiwgIiNmZmZmMzMiKQoKcHJlcF9kYXRhIDwtIGZ1bmN0aW9uKGdlbyl7CiAgZGF0YSA8LSBnZW9AZGF0YSAlPiUgcmVwbGFjZShpcy5uYSguKSwgMCkKICBkYXRhIDwtIHJlbmFtZShkYXRhLAogICAgdG90YWw9dl9DQTExTl8yNjUsCiAgICBBbWVyaWNhcz12X0NBMTFOXzI2OCwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiAgICBFdXJvcGU9dl9DQTExTl8zMDQsCiAgICBBZnJpY2E9dl9DQTExTl8zMzQsCiAgICBQaGlsaXBwaW5lcz12X0NBMTFOXzM3NiwKICAgIENoaW5hPXZfQ0ExMU5fMzc5LAogICAgSW5kaWE9dl9DQTExTl8zODIpCiAgZGF0YSA8LSBtdXRhdGUoZGF0YSxgT3RoZXIgQXNpYW4gQ291bnRyaWVzYCA9IHZfQ0ExMU5fMzczLVBoaWxpcHBpbmVzLUNoaW5hLUluZGlhKQogIGdlb0BkYXRhIDwtIGRhdGEKICByZXR1cm4oZ2VvKQp9CmBgYAoKCk5leHQgd2UgZ3JhYiB0aGUgZGF0YSB2aWEgYGNhbmNlbnN1c2AsCmBgYHtyLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmRhdGFfY3NkPWdldF9jZW5zdXMoZGF0YXNldCA9ICdDQTExJywgcmVnaW9ucz1yZWdpb25zLHZlY3RvcnM9dmVjdG9ycyxnZW9fZm9ybWF0PSdzcCcsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0NTRCcpICU+JSBwcmVwX2RhdGEKZGF0YV9jdD1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExMScsIHJlZ2lvbnM9cmVnaW9ucyx2ZWN0b3JzPXZlY3RvcnMsZ2VvX2Zvcm1hdD0nc3AnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdDVCcpICU+JSBwcmVwX2RhdGEKZGF0YV9kYT1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExMScsIHJlZ2lvbnM9cmVnaW9ucyx2ZWN0b3JzPXZlY3RvcnMsZ2VvX2Zvcm1hdD0nc3AnLGxhYmVscz0nc2hvcnQnLGxldmVsPSdEQScpICU+JSBwcmVwX2RhdGEKZGF0YV9kYj1nZXRfY2Vuc3VzKGRhdGFzZXQgPSAnQ0ExMScsIHJlZ2lvbnM9cmVnaW9ucyxnZW9fZm9ybWF0PSdzcCcsbGFiZWxzPSdzaG9ydCcsbGV2ZWw9J0RCJykKYGBgCgp3aGljaCB3ZSB0aGVuIHJlLWFnZ3JlZ2F0ZSB0byBtYWtlIHN1cmUgd2UgZG9uJ3QgbWlzcyBvdmVyYWxsIGNvdW50cyBkdWUgdG8gcHJpdmFjeSBjdXRvZmZzIGRpc3RyaWJ1dGUgdGhlbQpwcm9wb3J0aW9uYWxseSBhbW9uZyB0aGUgcG9wdWxhdGlvbi4KYGBge3J9CmRhdGFfY3RAZGF0YSA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9jdEBkYXRhLHBhcmVudF9kYXRhPWRhdGFfY3NkQGRhdGEsZ2VvX21hdGNoPXNldE5hbWVzKCJHZW9VSUQiLCJDU0RfVUlEIiksY2F0ZWdvcmllcz1jYXRlZ29yaWVzLGJhc2U9IlBvcHVsYXRpb24iKQpkYXRhX2RhQGRhdGEgPC0gZG90X2RlbnNpdHkucHJvcG9ydGlvbmFsX3JlX2FnZ3JlZ2F0ZShkYXRhPWRhdGFfZGFAZGF0YSxwYXJlbnRfZGF0YT1kYXRhX2N0QGRhdGEsZ2VvX21hdGNoPXNldE5hbWVzKCJHZW9VSUQiLCJDVF9VSUQiKSxjYXRlZ29yaWVzPWNhdGVnb3JpZXMsYmFzZT0iUG9wdWxhdGlvbiIpCmRhdGFfZGJAZGF0YSA8LSBkb3RfZGVuc2l0eS5wcm9wb3J0aW9uYWxfcmVfYWdncmVnYXRlKGRhdGE9ZGF0YV9kYkBkYXRhLHBhcmVudF9kYXRhPWRhdGFfZGFAZGF0YSxnZW9fbWF0Y2g9c2V0TmFtZXMoIkdlb1VJRCIsIkRBX1VJRCIpLGNhdGVnb3JpZXM9Y2F0ZWdvcmllcyxiYXNlPSJQb3B1bGF0aW9uIikKYGBgCgoKYGBge3IsIGVjaG89RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoZ2dwbG90MikKYmdfY29sb3I9IiMxMTExMTEiCmJhc2VfY29sb3I9IiMzMzMzMzMiCnRleHRfY29sb3I9IiNlZWVlZWUiCnRoZW1lX29wdHM8LWxpc3QodGhlbWUocGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGJnX2NvbG9yLCBjb2xvdXIgPSBOQSksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGw9YmdfY29sb3IsIHNpemU9MSxsaW5ldHlwZT0ic29saWQiLGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTgwLGhqdXN0ID0gMC41LCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemU9NjAsaGp1c3QgPSAwLjUsIGNvbG9yPXRleHRfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChzaXplPTI1LCBjb2xvcj10ZXh0X2NvbG9yKSwKICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT00MCwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZT00MCwgY29sb3I9dGV4dF9jb2xvciksCiAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbD1iZ19jb2xvciwgc2l6ZT0xLGxpbmV0eXBlPSJzb2xpZCIsY29sb3I9YmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkgPSBlbGVtZW50X3JlY3QoZmlsbCA9IGJnX2NvbG9yLGNvbG9yID0gYmdfY29sb3IpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5rZXkud2lkdGggPSB1bml0KDMsICdsaW5lcycpLAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSkKCmJhc2VtYXAgPC0gICBnZ3Bsb3QoZGF0YV9jc2QpICsKICAgIGdlb21fcG9seWdvbihhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbCA9IGJhc2VfY29sb3IsIHNpemU9MC4xLCBjb2xvciA9ICdncmV5JykgKwogICAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2xlZ2VuZChucm93PTEsb3ZlcnJpZGUuYWVzID0gbGlzdChzaXplPTE1KSkpICsKICAgIGNvb3JkX21hcChwcm9qZWN0aW9uPSJsYW1iZXJ0IiwgbGF0MD00OSwgbGF0PTQ5LjQpICsKICAgIHRoZW1lX29wdHMKCmBgYAoKCiMjTWFwCkFsbCB0aGF0J3MgbGVmdCB0byBkbyBpcyB0byBjb3ZlcnQgb3VyIHJlLWFnZ3JlZ2F0ZWQgYmxvY2stbGV2ZWwgZGF0YSB0byBkb3RzLCB1c2luZyB0aGUgYGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90c2AKZnVuY3Rpb24gZnJvbSB0aGUgW2Bkb3RkZW5zaXR5YCBwYWNrYWdlXSgpIGFuZCBmZWVkIGl0IGludG8gdGhlIGBkb3RfZGVuc2l0eS5kb3RzX21hcGAgZnVuY3Rpb24gdG8gYWRkIHRoZW0gdG8Kb3VyIGJhc2VtYXAuCmBgYHtyLCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTMsIG1lc3NhZ2U9VFJVRSwgd2FybmluZz1UUlVFfQojIDEgZG90ID0gNSBpbW1pZ3JhbnRzCnNjYWxlPTUKCgpkb3RzLmRiIDwtIGRvdF9kZW5zaXR5LmNvbXB1dGVfZG90cyhnZW9fZGF0YSA9IGRhdGFfZGIsIGNhdGVnb3JpZXMgPSBjYXRlZ29yaWVzLCBzY2FsZT1zY2FsZSkKYmFzZW1hcCArCiAgIyB6b29tIGluIGEgYml0CiAgY29vcmRfZml4ZWQoeGxpbT1jKC0xMjMuMjksLTEyMi42KSwgeWxpbT1jKDQ5LjAyLDQ5LjM1KSwgcmF0aW8gPSAxL2Nvcyg0OS4yLzE4MCpwaSkpICsKIyBzaGFkZSB1bnBvcHVsYXRlZCBibG9ja3MKIyAgZ2VvbV9wb2x5Z29uKGRhdGE9ZGF0YV9kYltkYXRhX2RiJFBvcHVsYXRpb248PTUsXSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBhZXMobG9uZywgbGF0LCBncm91cCA9IGdyb3VwKSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gIiMyMjIyMjIiLCBzaXplPTAuMSwgCiMgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICIjMjIyMjIyIikgKwogIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzID0gY29sb3JzKSArCiAgbGFicyhjb2xvciA9ICIiLAogICAgICAgICAgICAgICAgdGl0bGU9IkltbWlncmFudHMgMjAwNiAtIDIwMTEiLAogICAgICAgICAgICAgICAgY2FwdGlvbj0iU291cmNlOiBTdGF0Q2FuIENlbnN1cyAyMDE2IHZpYSBjYW5jZW5zdXMgJiBDZW5zdXNNYXBwZXIuY2EiLAogICAgICAgICAgICAgICAgc3VidGl0bGUgPSBwYXN0ZTAoIjEgZG90ID0gIixzY2FsZSwiIHBlb3BsZSIpKSArIAogIGRvdF9kZW5zaXR5LmRvdHNfbWFwKGRvdHM9ZG90cy5kYixhbHBoYT0wLjc1LHNpemU9MC4yNSkKCiMgc2F2ZSBpbWFnZSBmb3IgbGF0ZXIKZ2dzYXZlKCcuLi9pbWFnZXMvcmVjZW50X2ltbWlncmFudHMucG5nJyx3aWR0aD0yNixoZWlnaHQ9MjYpCmBgYAoK